package jmind.core.aspect;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.google.common.reflect.TypeToken;
import jmind.base.cache.ICache;
import jmind.base.util.DataUtil;
import jmind.base.util.GlobalConstants;
import jmind.base.annotation.CacheMonitor;
import jmind.core.cache.support.Cache;
import jmind.core.manager.RedisManager;
import jmind.core.redis.Redis;
import jmind.core.support.MethodSupport;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.lang.reflect.Method;
import java.util.*;

/**
 * @author wbxie
 * @CacheMonitor( key = "'user",exp=3600)
 * @CacheMonitor(prefix ="user", exp = GlobalConstants.HOUR, spel = "mac")
 * key 用做前缀+ 方法参数值
 * @Component
 * @Aspect 好像什么时候都需要打这个标签
 * 2013-12-4
 */
@Aspect
public class CacheAspect {

  //  public static Map<ProceedingJoinPoint, Class> MS = Maps.newConcurrentMap();


    public static Map<Method, Class> M = Maps.newConcurrentMap();



    @Around(value = "execution(* *(..)) && @annotation(monitor)", argNames = "pjp,monitor")
    public Object doAround(ProceedingJoinPoint pjp,CacheMonitor monitor) throws Throwable {



        // MethodSignature signature = (MethodSignature) pjp.getSignature();
        // System.out.println(signature.getDeclaringTypeName() + "&" + signature.getName() + "&"
        //         + signature.toLongString() + "&" + signature.toShortString() + "&" + signature.getDeclaringType());
        //  System.out.println(signature.getMethod().getName()+"%"+signature.getReturnType());
        //      com.zhimei.platform.web.controller.TestController&test2&public java.lang.String com.zhimei.platform.web.controller.TestController.test2(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)&TestController.test2(..)&class com.zhimei.platform.web.controller.TestController
        //      test2%class java.lang.String
        //      DBObject arr = new BasicDBObject("cla", signature.getDeclaringTypeName());
        //      arr.put("method", signature.getName());
        //      arr.put("arg", pjp.getArgs()[0]);
        //      arr.put("exp", m.type().name());
        //      arr.put("key", key);
        //      System.out.println(arr);

        // key 用做前缀 ,当spel不为空时，取第一个对象的属性值
        final String key = monitor.key().isEmpty() ? DataUtil.composeKey(monitor.prefix(), pjp.getArgs()) : monitor.prefix()
                + GlobalConstants.DASH + MethodSupport.getProperty(pjp.getArgs()[0], monitor.key());

        if (monitor.type() == ICache.Type.REDIS) {
            return doRedis(pjp, monitor, key);
        }

        ICache cache = Cache.getCache().getCache(monitor.type(), monitor.name());
        //添加参数值 做为key
        if (monitor.remove()) {
            Object object = pjp.proceed();
            cache.delete(key);
            return object;
        }

        return cache.computeIfAbsent(key, monitor.exp(), k -> {
            try {
                return pjp.proceed();
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                return null;
            }
        });


    }

    @SuppressWarnings("unchecked")
    private final Object doRedis(final ProceedingJoinPoint pjp, final CacheMonitor m, final String key) throws Throwable {

        Redis cache = RedisManager.getInstance().getResource(m.name());
        if (m.remove()) {
            Object object = pjp.proceed();
            cache.del(key);
            return object;
        }

        String obj = cache.get(key);
        if (obj == null) {
            Object ob = pjp.proceed();
            if (ob != null)
                cache.setex(key, m.exp(), JSON.toJSONString(ob));
            return ob;
        } else {
            MethodSignature signature = (MethodSignature) pjp.getSignature();

            @SuppressWarnings("rawtypes")
            Class returnType = signature.getReturnType();
         //   MS.put(pjp, returnType);
            if (returnType.equals(String.class)) { // String 直接返回
                return obj;
            } else if (List.class.isAssignableFrom(returnType)) { // List 特许处理
                List<?> list = JSON.parseArray(obj, getRawType(signature, returnType));
                if (!returnType.isInterface() && !returnType.isInstance(list)) {
                    List o = (List) returnType.newInstance();
                    o.addAll(list);
                    return o;
                }
                return list;
            } else if (Set.class.isAssignableFrom(returnType)) {
                List list = JSON.parseArray(obj, getRawType(signature, returnType));
                if (returnType.isInterface()) {
                    return new HashSet<>(list);
                }
                Set set = (Set) returnType.newInstance();
                set.addAll(list);
                return set;
            } else if (Map.class.isAssignableFrom(returnType)) {
                // if(aClass.equals(String.class) || Number.class.isAssignableFrom(aClass))
                Map map = JSON.parseObject(obj, Map.class);
                Class aClass = getRawType(signature, returnType, 1);
                if (!map.isEmpty() && map.values().iterator().next().getClass() != aClass) {
                    Map mm = newMap(aClass);
                    map.forEach((k, v) -> mm.put(k, JSON.parseObject(v.toString(), aClass)));
                    return mm;
                }
                return map;
            }
            // Primitive,Number,Map,List,set 都能支持
            return JSON.parseObject(obj, signature.getReturnType());

        }

    }


    private final <T> Map<String, T> newMap(Class<T> clazz) {
        return new HashMap<String, T>();
    }

    private final Class getRawType(MethodSignature signature, Class returnType, int index) {
        return M.computeIfAbsent(signature.getMethod(), m -> {
            java.lang.reflect.Type type = m.getGenericReturnType();
            TypeToken<?> type2 = TypeToken.of(type).resolveType(returnType.getTypeParameters()[index]);
            return type2.getRawType();
        });


    }

    private final Class getRawType(MethodSignature signature, Class returnType) {
        return getRawType(signature, returnType, 0);
    }

  /* public Object doRedis(ProceedingJoinPoint pjp) throws Throwable {
        final CacheMonitor m = JoinPoints.getAnnotation(pjp, CacheMonitor.class);
        String key = m.spel().isEmpty() ? DataUtil.composeKey(m.prefix(), pjp.getArgs()) : m.prefix()
                + GlobalConstants.DASH + MethodSupport.getProperty(pjp.getArgs()[0], m.spel());
        Object obj=pjp.proceed();

        String obj2=JSON.toJSONString(obj);

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        @SuppressWarnings("rawtypes")
        Class returnType = signature.getReturnType();
        if (returnType.equals(String.class)) { // String 直接返回
            return obj;
        } else if(Set.class.isAssignableFrom(returnType)) {

            java.lang.reflect.Type type = signature.getMethod().getGenericReturnType();
            TypeToken<?> type2 = TypeToken.of(type).resolveType(Set.class.getTypeParameters()[0]);
            System.err.println("set="+type2.getRawType());
            List list = JSON.parseArray(obj2, type2.getRawType());
            if(returnType.isInterface()){
                return new HashSet<>(list);
            }
            Set set= (Set) returnType.newInstance();
            set.addAll(list);
            return set;
        } else if (List.class.isAssignableFrom(returnType)) { // List 特许处理

            java.lang.reflect.Type type = signature.getMethod().getGenericReturnType();
            System.err.println("ff="+returnType);
            TypeToken<?> type2 = TypeToken.of(type).resolveType(List.class.getTypeParameters()[0]);
            System.err.println("list="+type2.getRawType());

            List<?> list = JSON.parseArray(obj2, type2.getRawType());
            if(!returnType.isInterface() && !returnType.isInstance(list)){
                System.err.println("sss");
                List o = (List) returnType.newInstance();
                o.addAll(list);
                return o;
            }
        }else if(Map.class.isAssignableFrom(returnType)){
            return  JSON.parseObject(obj2,Map.class);
        }

        return JSON.parseObject(obj2, returnType);
    }
*/


}
